1 module range_primitives_helper;
2 
3 import std.algorithm.iteration : map, filter, joiner;
4 import std.algorithm.searching : any;
5 import std.conv : to;
6 import std.array;
7 import std.range;
8 import std.traits;
9 
10 @safe:
11 
12 /**
13 * This function produces a descriptive message why `R` is not an InputRange.
14 * If `R` is an InputRange the returned string will say so.
15 */
16 string isInputRangeErrorFormatter(R)() pure {
17 	static if(is(typeof(R.init) == R)) {
18 		string ret = R.stringof ~ " is not an InputRange because:\n\t";
19 
20 		Test[] tests =
21 				[ isValidEmpty!R()
22 				, isFrontValid!R()
23 				, isPopFrontValid!R()
24 				];
25 
26 		auto msg = testsToString(tests);
27 
28 		bool failed = tests.map!(t => t.failed).any();
29 
30 		return failed
31 			? ret ~ msg
32 			: R.stringof ~ " is an InputRange";
33 	} else {
34 		return R.stringof ~ " can not be tested as " ~ R.stringof ~ ".init"
35 			~ " does not construct a compileable " ~ R.stringof;
36 	}
37 }
38 
39 /// ditto
40 unittest {
41 	struct Foo {}
42 	enum msg = isInputRangeErrorFormatter!(Foo);
43 	enum exp =`Foo is not an InputRange because:
44 	the property 'empty' does not exist
45 	and the property 'front' does not exist
46 	and the function 'popFront' does not exist`;
47 	static assert(msg == exp, msg ~ "\n" ~ exp);
48 	string msg2 = isInputRangeErrorFormatter!(Foo);
49 	assert(msg2 == exp, msg ~ "\n" ~ exp);
50 }
51 
52 /// ditto
53 unittest {
54 	struct Foo {
55 		int empty;
56 	}
57 	enum msg = isInputRangeErrorFormatter!(Foo);
58 	enum exp =`Foo is not an InputRange because:
59 	the property 'empty' is not of type 'bool' but 'int'
60 	and the property 'front' does not exist
61 	and the function 'popFront' does not exist`;
62 	static assert(msg == exp, msg ~ "\n" ~ exp);
63 	string msg2 = isInputRangeErrorFormatter!(Foo);
64 	assert(msg2 == exp, msg ~ "\n" ~ exp);
65 }
66 
67 /// ditto
68 unittest {
69 	struct Foo {
70 		bool empty;
71 		void front();
72 	}
73 	enum msg = isInputRangeErrorFormatter!(Foo);
74 	enum exp =`Foo is not an InputRange because:
75 	the property 'front' does not return a non 'void' value
76 	and the function 'popFront' does not exist`;
77 	static assert(msg == exp, msg ~ "\n" ~ exp);
78 	string msg2 = isInputRangeErrorFormatter!(Foo);
79 	assert(msg2 == exp, msg ~ "\n" ~ exp);
80 }
81 
82 /// ditto
83 unittest {
84 	struct Foo {
85 		bool empty;
86 		int front();
87 	}
88 	enum msg = isInputRangeErrorFormatter!(Foo);
89 	enum exp =`Foo is not an InputRange because:
90 	the function 'popFront' does not exist`;
91 	static assert(msg == exp, msg ~ "\n" ~ exp);
92 	string msg2 = isInputRangeErrorFormatter!(Foo);
93 	assert(msg2 == exp, msg ~ "\n" ~ exp);
94 }
95 
96 /// ditto
97 unittest {
98 	struct Foo {
99 		bool empty;
100 		int front();
101 		void popFront();
102 	}
103 	enum msg = isInputRangeErrorFormatter!(Foo);
104 	enum exp =`Foo is an InputRange`;
105 	static assert(msg == exp, msg ~ "\n" ~ exp);
106 	string msg2 = isInputRangeErrorFormatter!(Foo);
107 	assert(msg2 == exp, msg ~ "\n" ~ exp);
108 }
109 
110 /**
111 * This function produces a descriptive message why `R` is not a ForwardRange.
112 * If `R` is an ForwardRange the returned string will say so.
113 */
114 string isForwardRangeErrorFormatter(R)() pure {
115 	static if(is(typeof(R.init) == R)) {
116 		string ret = R.stringof ~ " is not an ForwardRange because\n\t";
117 
118 		Test[] tests =
119 				[ isValidEmpty!R()
120 				, isFrontValid!R()
121 				, isPopFrontValid!R()
122 				, isSaveValid!R()
123 				];
124 		auto msg = testsToString(tests);
125 
126 		bool failed = tests.map!(t => t.failed).any();
127 
128 		return failed
129 			? ret ~ msg
130 			: R.stringof ~ " is an ForwardRange";
131 	} else {
132 		return R.stringof ~ " can not be tested as " ~ R.stringof ~ ".init"
133 			~ " does not construct a compileable " ~ R.stringof;
134 	}
135 }
136 
137 /// ditto
138 unittest {
139 	struct Foo {}
140 	enum msg = isForwardRangeErrorFormatter!(Foo);
141 	enum exp =`Foo is not an ForwardRange because
142 	the property 'empty' does not exist
143 	and the property 'front' does not exist
144 	and the function 'popFront' does not exist
145 	and the property 'save' does not exist`;
146 	static assert(msg == exp, msg ~ "\n" ~ exp);
147 	string msg2 = isForwardRangeErrorFormatter!(Foo);
148 	assert(msg2 == exp, msg ~ "\n" ~ exp);
149 }
150 
151 /// ditto
152 unittest {
153 	struct Foo {
154 		int empty;
155 	}
156 	enum msg = isForwardRangeErrorFormatter!(Foo);
157 	enum exp =`Foo is not an ForwardRange because
158 	the property 'empty' is not of type 'bool' but 'int'
159 	and the property 'front' does not exist
160 	and the function 'popFront' does not exist
161 	and the property 'save' does not exist`;
162 	static assert(msg == exp, msg ~ "\n" ~ exp);
163 	string msg2 = isForwardRangeErrorFormatter!(Foo);
164 	assert(msg2 == exp, msg ~ "\n" ~ exp);
165 }
166 
167 /// ditto
168 unittest {
169 	struct Foo {
170 		bool empty;
171 		void front();
172 	}
173 	enum msg = isForwardRangeErrorFormatter!(Foo);
174 	enum exp =`Foo is not an ForwardRange because
175 	the property 'front' does not return a non 'void' value
176 	and the function 'popFront' does not exist
177 	and the property 'save' does not exist`;
178 	static assert(msg == exp, msg ~ "\n" ~ exp);
179 	string msg2 = isForwardRangeErrorFormatter!(Foo);
180 	assert(msg2 == exp, msg ~ "\n" ~ exp);
181 }
182 
183 /// ditto
184 unittest {
185 	struct Foo {
186 		bool empty;
187 		int front();
188 	}
189 	enum msg = isForwardRangeErrorFormatter!(Foo);
190 	enum exp =`Foo is not an ForwardRange because
191 	the function 'popFront' does not exist
192 	and the property 'save' does not exist`;
193 	static assert(msg == exp, msg ~ "\n" ~ exp);
194 	string msg2 = isForwardRangeErrorFormatter!(Foo);
195 	assert(msg2 == exp, msg ~ "\n" ~ exp);
196 }
197 
198 /// ditto
199 unittest {
200 	struct Foo {
201 		bool empty;
202 		int front();
203 		void popFront();
204 	}
205 	enum msg = isForwardRangeErrorFormatter!(Foo);
206 	enum exp =`Foo is not an ForwardRange because
207 	the property 'save' does not exist`;
208 	static assert(msg == exp, msg ~ "\n" ~ exp);
209 	string msg2 = isForwardRangeErrorFormatter!(Foo);
210 	assert(msg2 == exp, msg ~ "\n" ~ exp);
211 }
212 
213 /// ditto
214 unittest {
215 	struct Foo {
216 		bool empty;
217 		int front();
218 		void popFront();
219 		int save;
220 	}
221 	enum msg = isForwardRangeErrorFormatter!(Foo);
222 	enum exp =`Foo is not an ForwardRange because
223 	the property 'save' does not return a 'Foo' but a 'int'`;
224 	static assert(msg == exp, msg ~ "\n" ~ exp);
225 	string msg2 = isForwardRangeErrorFormatter!(Foo);
226 	assert(msg2 == exp, msg ~ "\n" ~ exp);
227 }
228 
229 /// ditto
230 unittest {
231 	struct Foo {
232 		bool empty;
233 		int front();
234 		void popFront();
235 		Foo save() { return this; }
236 	}
237 	enum msg = isForwardRangeErrorFormatter!(Foo);
238 	enum exp =`Foo is an ForwardRange`;
239 	static assert(msg == exp, msg ~ "\n" ~ exp);
240 	string msg2 = isForwardRangeErrorFormatter!(Foo);
241 	assert(msg2 == exp, msg ~ "\n" ~ exp);
242 }
243 
244 /**
245 * This function produces a descriptive message why `R` is not an BidirectionalRange.
246 * If `R` is an BidirectionalRange the returned string will say so.
247 */
248 string isBidirectionalRangeErrorFormatter(R)() pure {
249 	static if(is(typeof(R.init) == R)) {
250 		string ret = R.stringof ~ " is not an BidirectionalRange because\n\t";
251 
252 		Test[] tests =
253 				[ isValidEmpty!R()
254 				, isFrontValid!R()
255 				, isPopFrontValid!R()
256 				, isBackValid!R()
257 				, isPopBackValid!R()
258 				];
259 		auto msg = testsToString(tests);
260 
261 		bool failed = tests.map!(t => t.failed).any();
262 
263 		return failed
264 			? ret ~ msg
265 			: R.stringof ~ " is an BidirectionalRange";
266 	} else {
267 		return R.stringof ~ " can not be tested as " ~ R.stringof ~ ".init"
268 			~ " does not construct a compileable " ~ R.stringof;
269 	}
270 }
271 
272 /// ditto
273 unittest {
274 	struct Foo {}
275 	enum msg = isBidirectionalRangeErrorFormatter!(Foo);
276 	enum exp =`Foo is not an BidirectionalRange because
277 	the property 'empty' does not exist
278 	and the property 'front' does not exist
279 	and the function 'popFront' does not exist
280 	and the property 'back' does not exist
281 	and the function 'popBack' does not exist`;
282 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
283 	string msg2 = isBidirectionalRangeErrorFormatter!(Foo);
284 	assert(msg2 == exp, msg ~ "\n" ~ exp);
285 }
286 
287 /// ditto
288 unittest {
289 	struct Foo {
290 		int empty;
291 	}
292 	enum msg = isBidirectionalRangeErrorFormatter!(Foo);
293 	enum exp =`Foo is not an BidirectionalRange because
294 	the property 'empty' is not of type 'bool' but 'int'
295 	and the property 'front' does not exist
296 	and the function 'popFront' does not exist
297 	and the property 'back' does not exist
298 	and the function 'popBack' does not exist`;
299 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
300 	string msg2 = isBidirectionalRangeErrorFormatter!(Foo);
301 	assert(msg2 == exp, msg ~ "\n" ~ exp);
302 }
303 
304 /// ditto
305 unittest {
306 	struct Foo {
307 		bool empty;
308 		void front();
309 	}
310 	enum msg = isBidirectionalRangeErrorFormatter!(Foo);
311 	enum exp =`Foo is not an BidirectionalRange because
312 	the property 'front' does not return a non 'void' value
313 	and the function 'popFront' does not exist
314 	and the property 'back' does not exist
315 	and the function 'popBack' does not exist`;
316 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
317 	string msg2 = isBidirectionalRangeErrorFormatter!(Foo);
318 	assert(msg2 == exp, msg ~ "\n" ~ exp);
319 }
320 
321 /// ditto
322 unittest {
323 	struct Foo {
324 		bool empty;
325 		int front();
326 	}
327 	enum msg = isBidirectionalRangeErrorFormatter!(Foo);
328 	enum exp =`Foo is not an BidirectionalRange because
329 	the function 'popFront' does not exist
330 	and the property 'back' does not exist
331 	and the function 'popBack' does not exist`;
332 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
333 	string msg2 = isBidirectionalRangeErrorFormatter!(Foo);
334 	assert(msg2 == exp, msg ~ "\n" ~ exp);
335 }
336 
337 /// ditto
338 unittest {
339 	struct Foo {
340 		bool empty;
341 		int front();
342 		void popFront();
343 		float back();
344 	}
345 	enum msg = isBidirectionalRangeErrorFormatter!(Foo);
346 	enum exp =`Foo is not an BidirectionalRange because
347 	the property 'back' does return a 'float' which is not equal to the type of 'front' which is 'int'
348 	and the function 'popBack' does not exist`;
349 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
350 	string msg2 = isBidirectionalRangeErrorFormatter!(Foo);
351 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
352 }
353 
354 /// ditto
355 unittest {
356 	struct Foo {
357 		bool empty;
358 		int front();
359 		void popFront();
360 		float back();
361 	}
362 	enum msg = isBidirectionalRangeErrorFormatter!(Foo);
363 	enum exp =`Foo is not an BidirectionalRange because
364 	the property 'back' does return a 'float' which is not equal to the type of 'front' which is 'int'
365 	and the function 'popBack' does not exist`;
366 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
367 	string msg2 = isBidirectionalRangeErrorFormatter!(Foo);
368 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
369 }
370 
371 /// ditto
372 unittest {
373 	struct Foo {
374 		bool empty;
375 		int front();
376 		void popFront();
377 		int back();
378 
379 	}
380 	enum msg = isBidirectionalRangeErrorFormatter!(Foo);
381 	enum exp =`Foo is not an BidirectionalRange because
382 	the function 'popBack' does not exist`;
383 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
384 	string msg2 = isBidirectionalRangeErrorFormatter!(Foo);
385 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
386 }
387 
388 /// ditto
389 unittest {
390 	struct Foo {
391 		bool empty;
392 		int front();
393 		void popFront();
394 		int back();
395 		void popBack();
396 
397 	}
398 	enum msg = isBidirectionalRangeErrorFormatter!(Foo);
399 	static assert(msg == "Foo is an BidirectionalRange", msg);
400 	string msg2 = isBidirectionalRangeErrorFormatter!(Foo);
401 	assert(msg2 == "Foo is an BidirectionalRange", msg);
402 }
403 
404 /**
405 * This function produces a descriptive message why `R` is not a
406 * RandomAccessRange.
407 * If `R` is an RandomAccessRange the returned string will say so.
408 */
409 string isRandomAccessRangeErrorFormatter(R)() pure {
410 	static if(is(typeof(R.init) == R)) {
411 		string ret = R.stringof ~ " is not an RandomAccessRange because\n\t";
412 
413 		static if(!(isBidirectionalRange!R || !isInfinite!R)) {
414 			Test isNotBiNorInf = Test(true
415 				, ".empty' must either be 'enum bool = "
416 				~ "true' or 'ReturnType!(" ~ R.stringof ~ ".back)' must be equal"
417 				~ " to 'ReturnType!(" ~ R.stringof ~ ".front)'");
418 		} else {
419 			Test isNotBiNorInf = Test(false, "");
420 		}
421 
422 		static if(!(hasLength!R || !isInfinite!R)) {
423 			Test hasLenOrInf = Test(true
424 				, ".empty' must either be 'enum bool = "
425 				~ "true' or '" ~ R.stringof
426 				~ ".length' must be of type 'size_t'");
427 		} else {
428 			Test hasLenOrInf = Test(false, "");
429 		}
430 
431 		static if(!is(typeof(lvalueOf!R[1]))) {
432 			Test allowOpIndex = Test(true
433 				, "must allow for array indexing, aka. [] access");
434 		} else {
435 			Test allowOpIndex = Test(false, "");
436 		}
437 
438 		static if(is(typeof(lvalueOf!R[1]))
439 				&& !is(typeof(lvalueOf!R[1]) == ElementType!R))
440 		{
441 			Test opIndexType = Test(true
442 				, R.stringof ~ "[1] is of type '"
443 				~ typeof(lvalueOf!R[1]) ~ "' but must be equal to '"
444 				~ R.stringof ~ ".front' which is '" ~ ElementType!(R).stringof
445 				~ "'");
446 		} else {
447 			Test opIndexType = Test(false, "");
448 		}
449 
450 		Test[] tests =
451 				[ isValidEmpty!R()
452 				, isFrontValid!R()
453 				, isPopFrontValid!R()
454 				, isSaveValid!R()
455 				, isNotSomeString!R()
456 				, isNotBiNorInf
457 				, hasLenOrInf
458 				, allowOpIndex
459 				, opIndexType
460 				];
461 
462 		auto msg = testsToString(tests);
463 
464 		bool failed = tests.map!(t => t.failed).any();
465 
466 		return failed
467 			? ret ~ msg
468 			: R.stringof ~ " is an RandomAccessRange";
469 	} else {
470 		return R.stringof ~ " can not be tested as " ~ R.stringof ~ ".init"
471 			~ " does not construct a compileable " ~ R.stringof;
472 	}
473 }
474 
475 /// ditto
476 unittest {
477 	struct Foo {}
478 	enum msg = isRandomAccessRangeErrorFormatter!(Foo);
479 	enum exp =`Foo is not an RandomAccessRange because
480 	the property 'empty' does not exist
481 	and the property 'front' does not exist
482 	and the function 'popFront' does not exist
483 	and the property 'save' does not exist
484 	and must allow for array indexing, aka. [] access`;
485 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
486 	string msg2 = isRandomAccessRangeErrorFormatter!(Foo);
487 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
488 }
489 
490 /// ditto
491 unittest {
492 	struct Foo {
493 		int empty;
494 	}
495 	enum msg = isRandomAccessRangeErrorFormatter!(Foo);
496 	enum exp = `Foo is not an RandomAccessRange because
497 	the property 'empty' is not of type 'bool' but 'int'
498 	and the property 'front' does not exist
499 	and the function 'popFront' does not exist
500 	and the property 'save' does not exist
501 	and must allow for array indexing, aka. [] access`;
502 	static assert(msg == exp, msg ~ "\n" ~ exp);
503 	string msg2 = isRandomAccessRangeErrorFormatter!(Foo);
504 	assert(msg2 == exp, msg ~ "\n" ~ exp);
505 }
506 
507 /// ditto
508 unittest {
509 	struct Foo {
510 		bool empty;
511 		void front();
512 	}
513 	enum msg = isRandomAccessRangeErrorFormatter!(Foo);
514 	enum exp =`Foo is not an RandomAccessRange because
515 	the property 'front' does not return a non 'void' value
516 	and the function 'popFront' does not exist
517 	and the property 'save' does not exist
518 	and must allow for array indexing, aka. [] access`;
519 	static assert(msg == exp, msg ~ "\n" ~ exp);
520 	string msg2 = isRandomAccessRangeErrorFormatter!(Foo);
521 	assert(msg2 == exp, msg ~ "\n" ~ exp);
522 }
523 
524 /// ditto
525 unittest {
526 	struct Foo {
527 		bool empty;
528 		int front();
529 	}
530 	enum msg = isRandomAccessRangeErrorFormatter!(Foo);
531 	enum exp =`Foo is not an RandomAccessRange because
532 	the function 'popFront' does not exist
533 	and the property 'save' does not exist
534 	and must allow for array indexing, aka. [] access`;
535 	static assert(msg == exp, msg ~ "\n" ~ exp);
536 	string msg2 = isRandomAccessRangeErrorFormatter!(Foo);
537 	assert(msg2 == exp, msg ~ "\n" ~ exp);
538 }
539 
540 /// ditto
541 unittest {
542 	struct Foo {
543 		bool empty;
544 		int front();
545 		void popFront();
546 		float back();
547 	}
548 	enum msg = isRandomAccessRangeErrorFormatter!(Foo);
549 	enum exp =`Foo is not an RandomAccessRange because
550 	the property 'save' does not exist
551 	and must allow for array indexing, aka. [] access`;
552 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
553 	string msg2 = isRandomAccessRangeErrorFormatter!(Foo);
554 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
555 }
556 
557 /// ditto
558 unittest {
559 	struct Foo {
560 		enum empty = true;
561 		int front();
562 		void popFront();
563 		float back();
564 	}
565 	enum msg = isRandomAccessRangeErrorFormatter!(Foo);
566 	enum exp =`Foo is not an RandomAccessRange because
567 	the property 'save' does not exist
568 	and must allow for array indexing, aka. [] access`;
569 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
570 	string msg2 = isRandomAccessRangeErrorFormatter!(Foo);
571 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
572 }
573 
574 /// ditto
575 unittest {
576 	struct Foo {
577 		enum empty = true;
578 		int front();
579 		void popFront();
580 		float back();
581 		Foo save() { return this; }
582 	}
583 	enum msg = isRandomAccessRangeErrorFormatter!(Foo);
584 	enum exp =`Foo is not an RandomAccessRange because
585 	must allow for array indexing, aka. [] access`;
586 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
587 	string msg2 = isRandomAccessRangeErrorFormatter!(Foo);
588 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
589 }
590 
591 /// ditto
592 unittest {
593 	struct Foo {
594 		enum empty = true;
595 		int front();
596 		void popFront();
597 		float back();
598 		Foo save() { return this; }
599 		int opIndex(size_t idx) { return 0; }
600 		size_t length() { return 0; }
601 	}
602 	enum msg = isRandomAccessRangeErrorFormatter!(Foo);
603 	enum exp =`Foo is an RandomAccessRange`;
604 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
605 	string msg2 = isRandomAccessRangeErrorFormatter!(Foo);
606 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
607 }
608 
609 /**
610 * This function produces a descriptive message why `R` is not an
611 * OutputRange.
612 * If `R` is an OutputRange the returned string will say so.
613 */
614 string isOutputRangeErrorFormatter(R,E)() pure {
615 	string ret = R.stringof ~ " is not an OutputRange because";
616 
617 	Test putTest = isOutputRangeValue!(R,E)();
618 
619 	return putTest.failed
620 		? ret ~ "\n\t" ~ putTest.message
621 		: R.stringof ~ " is an OutputRange";
622 }
623 
624 unittest {
625 	struct Foo {}
626 	enum msg = isOutputRangeErrorFormatter!(Foo,int);
627 	enum exp =`Foo is not an OutputRange because
628 	calling std.range.primitives.put(ref Foo, int) is not possible`;
629 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
630 	string msg2 = isOutputRangeErrorFormatter!(Foo,int);
631 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
632 }
633 
634 unittest {
635 	struct Foo {
636 		void put(int a) { }
637 	}
638 	enum msg = isOutputRangeErrorFormatter!(Foo,int);
639 	enum exp =`Foo is an OutputRange`;
640 	static assert(msg == exp, "\n" ~ msg ~ "\n" ~ exp);
641 	string msg2 = isOutputRangeErrorFormatter!(Foo,int);
642 	assert(msg2 == exp, "\n" ~ msg ~ "\n" ~ exp);
643 }
644 
645 private:
646 
647 struct Test {
648 	bool failed;
649 	string message;
650 }
651 
652 //
653 // isFrontValid
654 //
655 Test isFrontValid(R)() {
656 	enum hasFront = is(typeof((return ref R r) => r.front));
657 	static if(hasFront) {
658 		alias RTfront = ReturnType!((R r) => r.front);
659 		enum hasFrontIsVoid = is(RTfront == void);
660 		static if(hasFrontIsVoid) {
661 			return Test(true
662 				, "the property 'front' does not return a non 'void' "
663 				~ "value");
664 		} else {
665 			return Test(false, "");
666 		}
667 	} else {
668 		return Test(true, "the property 'front' does not exist");
669 	}
670 }
671 
672 unittest {
673 	struct Foo{}
674 	enum Test a = isFrontValid!Foo();
675 	static assert(a.failed);
676 	static assert(a.message == "the property 'front' does not exist"
677 			, a.message);
678 }
679 
680 unittest {
681 	struct Foo{
682 		int front;
683 	}
684 	enum Test a = isFrontValid!Foo();
685 	static assert(!a.failed);
686 	static assert(a.message.empty, a.message);
687 }
688 
689 //
690 // isBackValid
691 //
692 Test isBackValid(R)() {
693 	enum hasBack = is(typeof((return ref R r) => r.back));
694 	static if(hasBack) {
695 		alias RTback = ReturnType!((R r) => r.back);
696 		enum hasBackIsEqualToFront = is(RTback == ElementType!R);
697 		static if(!hasBackIsEqualToFront) {
698 			return Test(true
699 				, "the property 'back' does return a '" ~ RTback.stringof
700 				~ "' which is not equal to the type of 'front' which is '"
701 				~ ElementType!(R).stringof ~ "'");
702 		} else {
703 			return Test(false, "");
704 		}
705 	} else {
706 		return Test(true, "the property 'back' does not exist");
707 	}
708 }
709 
710 unittest {
711 	struct Foo{}
712 	enum Test a = isBackValid!Foo();
713 	static assert(a.failed);
714 	static assert(a.message == "the property 'back' does not exist"
715 			, a.message);
716 }
717 
718 unittest {
719 	struct Foo{
720 		int back;
721 	}
722 	enum Test a = isBackValid!Foo();
723 	static assert(a.failed);
724 	static assert(a.message == "the property 'back' does return a 'int' which is not equal to the type of 'front' which is 'void'"
725 			, a.message);
726 }
727 
728 unittest {
729 	struct Foo{
730 		int back;
731 		double front;
732 	}
733 	enum Test a = isBackValid!Foo();
734 	static assert(a.failed);
735 	static assert(a.message == "the property 'back' does return a 'int' which is not equal to the type of 'front' which is 'double'"
736 			, a.message);
737 }
738 
739 unittest {
740 	struct Foo{
741 		int back;
742 		int front;
743 	}
744 	enum Test a = isBackValid!Foo();
745 	static assert(!a.failed);
746 	static assert(a.message.empty, a.message);
747 }
748 
749 //
750 // isPopFrontValid
751 //
752 Test isPopFrontValid(R)() {
753 	enum hasPopFront = is(typeof((R r) => r.popFront));
754 	static if(!hasPopFront) {
755 		return Test(true, "the function 'popFront' does not exist");
756 	} else {
757 		return Test(false, "");
758 	}
759 }
760 
761 unittest {
762 	struct Foo{}
763 	enum Test a = isPopFrontValid!Foo();
764 	static assert(a.failed);
765 	static assert(a.message == "the function 'popFront' does not exist"
766 			, a.message);
767 }
768 
769 unittest {
770 	struct Foo{
771 		void popFront();
772 	}
773 	enum Test a = isPopFrontValid!Foo();
774 	static assert(!a.failed);
775 	static assert(a.message.empty);
776 }
777 
778 //
779 // isPopBackValid
780 //
781 Test isPopBackValid(R)() {
782 	enum hasPopBack = is(typeof((R r) => r.popBack));
783 	static if(!hasPopBack) {
784 		return Test(true, "the function 'popBack' does not exist");
785 	} else {
786 		return Test(false, "");
787 	}
788 }
789 
790 unittest {
791 	struct Foo{}
792 	enum Test a = isPopBackValid!Foo();
793 	static assert(a.failed);
794 	static assert(a.message == "the function 'popBack' does not exist"
795 			, a.message);
796 }
797 
798 unittest {
799 	struct Foo{
800 		void popBack();
801 	}
802 	enum Test a = isPopBackValid!Foo();
803 	static assert(!a.failed);
804 	static assert(a.message.empty);
805 }
806 
807 //
808 // isSaveValid
809 //
810 Test isSaveValid(R)() {
811 	enum hasSave = is(typeof((R r) => r.save));
812 	static if(hasSave) {
813 		alias RTsave = ReturnType!((R r) => r.save);
814 		enum isSaveR = is(RTsave == R);
815 		static if(!isSaveR) {
816 			return Test(true,
817 			"the property 'save' does not return a '" ~ R.stringof
818 				~ "' but a '" ~ RTsave.stringof ~ "'");
819 		} else {
820 			return Test(false, "");
821 		}
822 	} else {
823 		return Test(true, "the property 'save' does not exist");
824 	}
825 }
826 
827 unittest {
828 	struct Foo{}
829 	enum Test a = isSaveValid!Foo();
830 	static assert(a.failed);
831 	static assert(a.message == "the property 'save' does not exist");
832 }
833 
834 unittest {
835 	struct Foo{
836 		int save;
837 	}
838 	enum Test a = isSaveValid!Foo();
839 	static assert(a.failed);
840 	static assert(a.message == "the property 'save' does not return a 'Foo'"
841 			~ " but a 'int'");
842 }
843 
844 //
845 // isValidEmpty
846 //
847 Test isValidEmpty(R)() {
848 	enum hasEmpty = __traits(hasMember, R, "empty");
849 	static if(hasEmpty) {
850 		static if (__traits(compiles, { enum e = R.empty; })) {
851 			enum bool b = R.empty;
852 			static if(b) {
853 				return Test(R.empty == false, "");
854 			} else {
855 				return Test(true, "the enum 'empty' must not be 'false'");
856 			}
857 		} else if(is(typeof(__traits(getMember, R, "empty")) == bool)) {
858 			return Test(false, "");
859 		} else {
860 			return Test(true, "the property 'empty' is not of type 'bool' but '"
861 					~ typeof(R.empty).stringof ~ "'");
862 		}
863 	} else {
864 		return Test(true, "the property 'empty' does not exist");
865 	}
866 }
867 
868 unittest {
869 	struct Foo {}
870 	enum Test a = isValidEmpty!Foo();
871 	static assert(a.failed);
872 	static assert(a.message == "the property 'empty' does not exist"
873 			, a.message);
874 }
875 
876 unittest {
877 	struct Foo {
878 		int empty;
879 	}
880 	enum Test a = isValidEmpty!Foo();
881 	static assert(a.failed);
882 	static assert(a.message ==
883 			"the property 'empty' is not of type 'bool' but 'int'"
884 			, a.message);
885 }
886 
887 unittest {
888 	struct Foo {
889 		enum bool empty = false;
890 	}
891 	enum Test a = isValidEmpty!Foo();
892 	static assert(a.failed);
893 	static assert(a.message == "the enum 'empty' must not be 'false'"
894 			, a.message);
895 }
896 
897 unittest {
898 	struct Foo {
899 		bool empty;
900 	}
901 	enum Test a = isValidEmpty!Foo();
902 	static assert(!a.failed);
903 	static assert(a.message.empty);
904 }
905 
906 unittest {
907 	struct Foo {
908 		enum bool empty = true;
909 	}
910 	enum Test a = isValidEmpty!Foo();
911 	static assert(!a.failed, a.message);
912 	static assert(a.message.empty);
913 }
914 
915 //
916 // isValidBoolEnum
917 //
918 Test isValidBoolEnum(R)() {
919 	static if (__traits(compiles, { enum e = R.empty; })) {
920 		enum bool b = R.empty;
921 		static if(b) {
922 			return Test(R.empty == false, "");
923 		} else {
924 			return Test(true, "the enum 'empty = true' must not be 'false'");
925 		}
926 	} else {
927 		return Test(true, "the enum 'empty = true' does not exist");
928 	}
929 }
930 
931 unittest {
932 	struct Foo {}
933 	enum Test a = isValidBoolEnum!Foo();
934 	static assert(a.failed);
935 	static assert(a.message == "the enum 'empty = true' does not exist"
936 			, a.message);
937 }
938 
939 unittest {
940 	struct Foo {
941 		int empty;
942 	}
943 	enum Test a = isValidBoolEnum!Foo();
944 	static assert(a.failed);
945 	static assert(a.message == "the enum 'empty = true' does not exist"
946 			, a.message);
947 }
948 
949 unittest {
950 	struct Foo {
951 		enum bool empty = false;
952 	}
953 	enum Test a = isValidBoolEnum!Foo();
954 	static assert(a.failed);
955 	static assert(a.message == "the enum 'empty = true' must not be 'false'"
956 			, a.message);
957 }
958 
959 unittest {
960 	struct Foo {
961 		bool empty;
962 	}
963 	enum Test a = isValidBoolEnum!Foo();
964 	static assert(a.failed);
965 	static assert(a.message == "the enum 'empty = true' does not exist"
966 			, a.message);
967 }
968 
969 unittest {
970 	struct Foo {
971 		enum bool empty = true;
972 	}
973 	enum Test a = isValidBoolEnum!Foo();
974 	static assert(!a.failed, a.message);
975 	static assert(a.message.empty);
976 }
977 
978 //
979 // isValidOpIndex
980 //
981 Test isValidOpIndex(R)() {
982 	static if(!is(typeof(lvalueOf!R[1]))) {
983 		return Test(true, "[] aka. opIndex not possible");
984 	} else static if(is(typeof(lvalueOf!R[1]))
985 			&& !is(typeof(lvalueOf!R[1]) == ElementType!R))
986 	{
987 		return Test(true
988 			, R.stringof ~ "[idx] is of type '"
989 			~ typeof(lvalueOf!R[1]).stringof ~ "' but must be equal to '"
990 			~ R.stringof ~ ".front' which is '" ~ ElementType!(R).stringof
991 			~ "'");
992 	} else {
993 		return Test(false, "");
994 	}
995 }
996 
997 unittest {
998 	struct Foo {}
999 	enum Test a = isValidOpIndex!Foo();
1000 	static assert(a.failed);
1001 	static assert(a.message == "[] aka. opIndex not possible", a.message);
1002 }
1003 
1004 unittest {
1005 	struct Foo {
1006 		int opIndex(size_t i) { assert(false); }
1007 	}
1008 	enum Test a = isValidOpIndex!Foo();
1009 	static assert(a.failed);
1010 	static assert(a.message == "Foo[idx] is of type 'int' but must be equal to 'Foo.front' which is 'void'");
1011 }
1012 
1013 unittest {
1014 	struct Foo {
1015 		double front;
1016 		int opIndex(size_t i) { assert(false); }
1017 	}
1018 	enum Test a = isValidOpIndex!Foo();
1019 	static assert(a.failed);
1020 	static assert(a.message == "Foo[idx] is of type 'int' but must be equal to 'Foo.front' which is 'double'");
1021 }
1022 
1023 unittest {
1024 	struct Foo {
1025 		int front;
1026 		int opIndex(size_t i) { assert(false); }
1027 	}
1028 	enum Test a = isValidOpIndex!Foo();
1029 	static assert(!a.failed);
1030 	static assert(a.message);
1031 }
1032 
1033 //
1034 // hasValidLength
1035 //
1036 Test hasValidLength(R)() {
1037 	static if(hasLength!R) {
1038 		return Test(false, "");
1039 	} else {
1040 		return Test(true, "property 'size_t length' is not defined");
1041 	}
1042 }
1043 
1044 unittest {
1045 	struct Foo {}
1046 	enum Test a = hasValidLength!Foo();
1047 	static assert(a.failed);
1048 	static assert(a.message == "property 'size_t length' is not defined"
1049 			, a.message);
1050 }
1051 
1052 unittest {
1053 	struct Foo {
1054 		double length;
1055 	}
1056 	enum Test a = hasValidLength!Foo();
1057 	static assert(a.failed);
1058 	static assert(a.message == "property 'size_t length' is not defined"
1059 			, a.message);
1060 }
1061 
1062 unittest {
1063 	struct Foo {
1064 		size_t length;
1065 	}
1066 	enum Test a = hasValidLength!Foo();
1067 	static assert(!a.failed);
1068 	static assert(a.message.empty);
1069 }
1070 
1071 //
1072 // isNotSomeString
1073 //
1074 Test isNotSomeString(R)() {
1075 	static if(isAutodecodableString!R || !isAggregateType!R) {
1076 		return Test(true, R.stringof ~ " must not be an autodecodable string"
1077 			~ " but should be an aggregate type");
1078 	} else {
1079 		return Test(false, "");
1080 	}
1081 }
1082 
1083 //
1084 // isValidOutputRange
1085 //
1086 Test isOutputRangeValue(R, E)() {
1087 	static if(!is(typeof(put(lvalueOf!R, lvalueOf!E)))) {
1088 		return Test(true, "calling std.range.primitives.put(ref " ~ R.stringof
1089 				~ ", " ~ E.stringof ~ ") is not possible");
1090 	} else {
1091 		return Test(false, "");
1092 	}
1093 }
1094 
1095 //
1096 // helper
1097 //
1098 
1099 string testsToString(Test[] tests) pure {
1100 	return tests
1101 		.filter!(t => t.failed)
1102 		.map!(t => t.message)
1103 		.joiner("\n\tand ")
1104 		.to!string();
1105 }
1106 
1107 void doPutCopyPast(R, E)(ref R r, auto ref E e)
1108 {
1109     static if (is(PointerTarget!R == struct))
1110         enum usingPut = hasMember!(PointerTarget!R, "put");
1111     else
1112         enum usingPut = hasMember!(R, "put");
1113 
1114     static if (usingPut)
1115     {
1116         static assert(is(typeof(r.put(e))),
1117             "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
1118         r.put(e);
1119     }
1120     else static if (isNarrowString!R && is(const(E) == const(typeof(r[0]))))
1121     {
1122         // one character, we can put it
1123         r[0] = e;
1124         r = r[1 .. $];
1125     }
1126     else static if (isNarrowString!R && isNarrowString!E && is(typeof(r[] = e)))
1127     {
1128         // slice assign. Note that this is a duplicate from put, but because
1129         // putChar uses doPut exclusively, we have to copy it here.
1130         immutable len = e.length;
1131         r[0 .. len] = e;
1132         r = r[len .. $];
1133     }
1134     else static if (isInputRange!R)
1135     {
1136         static assert(is(typeof(r.front = e)),
1137             "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
1138         r.front = e;
1139         r.popFront();
1140     }
1141     else static if (is(typeof(r(e))))
1142     {
1143         r(e);
1144     }
1145     else
1146     {
1147         static assert(false,
1148             "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
1149     }
1150 }